package com.zeratul.common.lock.aspect;


import cn.hutool.core.util.ObjectUtil;
import com.zeratul.common.core.exception.ServiceException;
import com.zeratul.common.core.utils.spring.SpelUtils;
import com.zeratul.common.lock.annotation.ZtLock;
import com.zeratul.common.lock.annotation.ZtLockType;
import com.zeratul.common.lock.codes.LockResultCodes;
import com.zeratul.common.lock.constants.LockConstants;
import com.zeratul.common.lock.utils.ZtLockUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;

import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * 分布式锁的切面
 */
@Aspect
@Order(Ordered.HIGHEST_PRECEDENCE + 100)
@Slf4j
public class ZtLockAspect {

    /**
     * 分布式锁处理增强，增强添加了@ZtLock增强的业务方法
     * @return
     */
    @Around("@annotation(ztLock)")
    public Object lockHandler(ProceedingJoinPoint joinPoint, ZtLock ztLock) throws Throwable {
        //获取当前方法签名
        MethodSignature signature = (MethodSignature)joinPoint.getSignature();
        Method method = signature.getMethod();

        //判断是否需要跳过分布式锁
        String nonLockEl = ztLock.nonLock();
        if (!ObjectUtil.isEmpty(nonLockEl)) {
            //如果表达式不为空，则判断表达式
            Boolean skipLock = SpelUtils.parseExpression(nonLockEl, joinPoint.getTarget(), method, joinPoint.getArgs(), Boolean.class, false);
            if (skipLock) {
                //确定跳过分布式锁的过程，直接调用目标方法
                return joinPoint.proceed();
            }
        }

        //获取锁的名称，根据Spel表达式
        String key = SpelUtils.parseExpression(ztLock.key(), joinPoint.getTarget(), method, joinPoint.getArgs(), String.class, null);
        if (ObjectUtil.isEmpty(key)) {
            //未解析出分布式锁的名称，直接调用目标方法
            return joinPoint.proceed();
        }
        //拼接前缀
        String lockName = LockConstants.ZT_LOCK_PREFIX + (ObjectUtil.isEmpty(ztLock.prefix()) ? "" : (ztLock.prefix() + ":")) + key;

        //获取分布式锁的类型
        ZtLockType dLockType = ztLock.lockType();

        //获取是否阻塞
        boolean isBlocking = ztLock.blocking();

        try {
            if (isBlocking) {
                //阻塞锁
                ZtLockUtils.lock(dLockType, lockName);
            } else {
                //非阻塞锁
                boolean flag = ZtLockUtils.tryLock(dLockType, lockName, ztLock.timeout());
                if (!flag) {
                    //没有获得分布式锁，则调用降级方法
                    return execFallBack(ztLock.fallback(), joinPoint.getTarget(), joinPoint.getArgs());
                }
            }

            //执行目标业务
            return joinPoint.proceed();
        } finally {
            //解除分布式锁
            ZtLockUtils.unlock();
        }
    }

    /**
     * 调用降级方法
     * @return
     */
    private Object execFallBack(String methodName, Object targetObj, Object[] args){
        //通过反射调用fallback方法
        try {
            //获取参数类型
            Class[] argCls = null;
            if (ObjectUtil.isNotEmpty(args)) {
                argCls = Arrays.stream(args)
                        .map(Object::getClass)
                        .toList()
                        .toArray(new Class[0]);
            }

            //执行目标方法
            Object result = targetObj.getClass()
                    .getMethod(methodName, argCls)
                    .invoke(targetObj, args);
            log.debug("【分布式锁】调用降级方法成功！返回结果：{}", result);
            return result;
        } catch (Exception e) {
            log.error("【分布式锁】调用降级方法失败！方法名称：{}", methodName, e);
            throw new ServiceException(LockResultCodes.NOT_GET_LOCK);
        }
    }
}
